class Position {
  type;
  index;
  $target;
  $item;
  playground;
  size;
  start;
  end;
  move;

  constructor(type) {
    this.type = type;
  }

  setGrid = (grid) => {
    __grid = grid;
  }

  setStart(event) {
    const $item = $(event.target).closest('.sullivan-grid-item');
    // 下标
    this.index = $item.index();
    // 目标元素
    this.$item = $item;
    // 目标元素主体
    this.$target = $item.find('.sullivan-grid-item-content');
    // 开始位置
    this.start = new Coordinate(event.pageX, event.pageY);
    // 移动距离
    this.move = new Coordinate(0, 0);
    // 大小
    this.size = new Size(this.$target);
    // 背景板
    this.playground = new Size($(event.target).closest('.sullivan-grid'));

    const initW = this.playground.initW / __grid.col
    const initH = this.playground.initH / __grid.row
    if (this.size.initW > initW) {
      this.size.setInitW($item, initW)
    }
    if (this.size.initH > initH) {
      this.size.setInitH($item, initH)
    }
  }

  setMove(event) {
    const { left, right, top, bottom } = this.playground
    /**
     * 第一层限制，必须在九宫格范围内。
     */
    if (event.pageX >= left && event.pageX <= right && event.pageY >= top && event.pageY <= bottom) {
      const { left, right, top, bottom } = this.size
      // console.log(this.$target.height(), this.size.initH, this.$target.height() >= this.size.initH)
      /**
       * 第二层限制，必须大于子区域大小。
       */
      if (this.$target.height() >= this.size.initH || this.$target.width() >= this.size.initW) {
        this.move.update(event.pageX - this.start.x, event.pageY - this.start.y);
        this.updateView()
      }
    }
    // console.log('move: => ', this.$target.height(), this.size.initH)
  }

  setEnd(event) {
    this.end = new Coordinate(event.pageX, event.pageY);
    this.setMove(event);
    this.updateView();
    this.updateLayout();
    this.size.update(this.$target);
    // console.log('end: => ', this)
  }

  /**
   * 拖动时，更新视图。
   */
  updateView () {
    const { width, height } = this.size
    const { x: left, y: top } = this.move
    switch (this.type) {
      case 'top': {
        return this.$target.css({ height: height - top, top })
      }
      case 'right': {
        return  this.$target.css({ width: width + left })
      }
      case 'bottom': {
        return this.$target.css({ height: height + top })
      }
      case 'left': {
        return this.$target.css({ width: width - left, left })
      }
    }
  }

  findHideActs (moveDistance, initSize, activated, gap, acts = []) {
    const __activated = activated.map(act => {
      const index = act + gap
      if (!activated.includes(index)) {
        const hideArea = __grid.areas[index];
        acts.push({ index, hideArea })
      }
      return index
    })

    if (moveDistance > initSize) {
      // 移动距离 > 基础值，递归添加下一个
      const __acts = this.findHideActs(moveDistance - initSize, initSize, __activated, gap)
      acts = [...acts, ...__acts];
    }

    return acts
  }

  /**
   * 吞并周边，放大
   *
   * @param { string } area 区域 key 值
   * @param { number } moveDistance 移动距离
   * @param { number } initSize 基础值，大于基础之，则会递归添加下一个
   * @param { number[] } activated 下标集合
   * @param { number } gap 下一个间隔，距离下一个下标的间隔
   * @param { string[] } hide 即将被隐藏的区域集合
   */
  updateGridAreas (area, moveDistance, initSize, activated, gap, hide = new Set()) {
    // const active = gap > 0 ? Math.max(...activated) : Math.min(...activated);
    let acts = this.findHideActs(moveDistance, initSize, [...activated], gap);
    const areasHides = __grid.findHideAreas();
    const hasHideItem = acts.find(({ hideArea }) => areasHides.includes(hideArea));

    // console.log('updateGridAreas:acts => ', acts, __grid.areasHides)
    console.log('updateGridAreas:hasHideItem => ', hasHideItem, areasHides, acts)
    if (hasHideItem) return;
    acts.map(({ index, hideArea }) => {
      hide.add(hideArea)
      __grid.update(index, area)
    })

    return acts
  }

  findShowAreas (moveDistance, initSize, activated, gap, acts = []) {
    let __activated;
    let prev = activated.shift();
    const showArea = __grid.areas[prev];
    acts.push({ index: prev, showArea});
    __activated = activated.filter(next => {
      if (next === prev + gap) {
        const showArea = __grid.areas[next];
        acts.push({ index: next, showArea});
        prev = next;
      } else {
        return next;
      }
    });

    if (moveDistance > initSize) {
      // 移动距离 > 基础值，递归删除下一个
      const [__acts, __activatedArr] = this.findShowAreas(moveDistance - initSize, initSize, __activated, gap);
      acts = [...acts, ...__acts];
      __activated = __activatedArr;
    }

    return [acts, __activated]
  }

  /**
   * 抛弃周边，缩小
   *
   * @param { number } moveDistance 移动距离
   * @param { number } initSize 基础值
   * @param { number[] } activated 下标集合
   * @param { number } gap 下一个间隔，距离下一个下标的间隔
   * @param { string[] } show 即将被显示的区域集合
   */
  removeGridAreas (moveDistance, initSize, activated, gap, show = new Set()) {
    const dataIndex = this.$item.attr('data-index');
    const [acts, __activated] = this.findShowAreas(moveDistance, initSize, [...activated], gap);

    acts.forEach(({ index, showArea}) => {
      show.add(showArea);
      __grid.update(index, showArea);
    });

    /**
     * 如果本次替换中包含自己，
     * 则将剩余部分，全部替换为最后一个。
     */
    const hasSelf = acts.find(({ index, showArea}) => index === dataIndex - 1);

    if (hasSelf && __activated.length) {
      let last = __activated.pop();
      const area = __grid.areas[last];
      __grid.update(last, area);

      __activated.forEach(index => {
        __grid.update(index, area);
      });

      __grid.replaceIndex(dataIndex, last);
    }
    console.log('removeGridAreas: => ', acts, gap)
    return show
  }

  /**
   * 寻找到当前选中项的所有下标位置
   *
   * @param originArea
   * @returns {*[]}
   */
  findActivated (originArea) {
    const areas = []
    __grid.areasCopy.forEach((area, index) => {
      if (area === originArea) {
        areas.push(index)
      }
    })
    // console.log('findActivated: => ', originArea, areas)
    return areas;
  }

  /**
   * 更新布局
   */
  updateLayout () {
    const area = this.$item.attr('data-area')
    const { initH, initW } = this.size
    const activated = this.findActivated(area)
    switch (this.type) {
      case 'top': {
        if (this.move.y < -20) {
          const hideAreas = this.updateGridAreas(area, -this.move.y, initH, activated, -__grid.row);
          // this.hideGridItem(hideAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:enlarge => ', activated, hideAreas)
        } else if (this.move.y > 20) {
          const showAreas = this.removeGridAreas(this.move.y, initH, activated, 1);
          // this.showGridItem(showAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:shrink => ', activated, showAreas)
        }
        break
      }
      case 'right': {
        if (this.move.x < -20) {
          const showAreas = this.removeGridAreas(-this.move.x, initW, activated.reverse(), -__grid.col);
          // this.showGridItem(showAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:shrink => ', activated, showAreas)
        } else if (this.move.x > 20) {
          const hideAreas = this.updateGridAreas(area, this.move.x, initW, activated, 1);
          // this.hideGridItem(hideAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:enlarge => ', activated, hideAreas)
        }
        break
      }
      case 'bottom': {
        if (this.move.y < -20) {
          const showAreas = this.removeGridAreas(-this.move.y, initH, activated.reverse(), -1);
          // this.showGridItem(showAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:shrink => ', activated, showAreas)
        } else if (this.move.y > 20) {
          const hideAreas = this.updateGridAreas(area, this.move.y, initH, activated, __grid.row);
          // this.hideGridItem(hideAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:enlarge => ', activated, hideAreas)
        }
        break
      }
      case 'left': {
        if (this.move.x < -20) {
          const hideAreas = this.updateGridAreas(area, -this.move.x, initW, activated, -1);
          // this.hideGridItem(hideAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:enlarge => ', activated, hideAreas)
        } else if (this.move.x > 20) {
          const showAreas = this.removeGridAreas(this.move.x, initW, activated, __grid.col);
          // this.showGridItem(showAreas);
          __grid.createGridLayout(true)
          // console.log('data-activated:shrink => ', activated, showAreas)
        }
        break
      }
    }

    // console.log('data-activated => ', __grid)
    this.$target.removeAttr('style')
  }
}